Anda mungkin pernah melihat istilah "augmented reality" digunakan dalam berbagai konteks. Jadi, kita harus memahami premis augmented reality sebelum kita mulai membahas detail implementasinya. Augmented Reality mengacu pada superposisi input yang dihasilkan komputer seperti citra, suara, grafik, dan teks di atas dunia nyata.
Augmented reality mencoba mengaburkan batas antara apa yang nyata dan apa yang dihasilkan komputer dengan menggabungkan informasi secara mulus dan meningkatkan apa yang kita lihat dan rasakan. Ini sebenarnya terkait erat dengan konsep yang disebut realitas termediasi dimana komputer memodifikasi pandangan kita tentang realitas. Akibatnya, teknologi bekerja dengan meningkatkan persepsi kita saat ini tentang realitas. Sekarang tantangannya di sini adalah membuatnya terlihat mulus bagi pengguna. Sangat mudah untuk hanya melapisi sesuatu di atas video input, tetapi kita perlu membuatnya terlihat seperti bagian dari video. Pengguna harus merasa bahwa input yang dihasilkan komputer mengikuti dunia nyata. Inilah yang ingin kitai capai ketika kita membangun sebuah sistem augmented reality.
Penelitian visi komputer dalam konteks ini mengeksplorasi bagaimana kita dapat menerapkan citra yang dihasilkan komputer ke live video streaming sehingga kita dapat meningkatkan persepsi dunia nyata. Teknologi augmented reality memiliki beragam aplikasi diantaranya: tampilan yang dipasang di kepala, automobile, visualisasi data, game, konstruksi, dan sebagainya. Sekarang kita memiliki ponsel cerdas yang kuat dan mesin yang lebih cerdas, kita dapat membangun aplikasi augmented reality high-calss dengan mudah.
Mari kita perhatikan gambar berikut:
Seperti yang bisa kita lihat di sini, kamera menangkap video dunia nyata untuk mendapatkan titik referensi. Sistem grafis menghasilkan objek virtual yang perlu dilapis di atas video. Sekarang blok penggabungan video adalah tempat semua keajaiban terjadi. Blok ini harus cukup pintar untuk memahami cara melapisi objek virtual di atas dunia nyata dengan cara sebaik mungkin.
Hasil augmented reality luar biasa, tetapi ada banyak hal matematika yang terjadi di bawahnya. Augmented reality menggunakan banyak transformasi geometris dan fungsi matematika terkait untuk memastikan semuanya terlihat mulus. Ketika berbicara tentang video langsung untuk augmented reality, kita perlu mendaftarkan objek virtual secara tepat di atas dunia nyata. Untuk memahaminya dengan lebih baik, mari kita anggap ini sebagai penyelarasan dua kamera, 1) kamera nyata yang digunakan untuk melihat dunia, dan 2) kamera virtual yang memproyeksikan objek grafis yang dihasilkan komputer.
Untuk membangun sistem augmented reality, transformasi geometris berikut perlu ditetapkan:
Object-to-scene: Transformasi ini mengacu pada transformasi koordinat 3D dari objek virtual dan mengekspresikannya dalam bingkai koordinat adegan dunia nyata kita. Ini memastikan bahwa kita memposisikan objek virtual di lokasi yang tepat.
Scene-to-camera: Transformasi ini mengacu pada pose kamera di dunia nyata. Yang kami maksud dengan "pose" adalah orientasi dan lokasi kamera. Kita perlu memperkirakan sudut pandang kamera sehingga kita tahu cara melapisi objek virtual.
Camera-ke-image: Ini mengacu pada parameter kalibrasi kamera. Ini mendefinisikan bagaimana kita dapat memproyeksikan objek 3D ke bidang gambar 2D. Ini adalah gambar yang benar-benar akan kita lihat pada akhirnya.
Perhatikan gambar berikut:
Seperti yang bisa kita lihat di sini, mobil itu mencoba masuk ke dalam scene tetapi terlihat sangat artifisial. Jika kita tidak mengubah koordinat dengan cara yang benar, maka hasilnya akan terlihat tidak wajar. Inilah yang kita bicarakan dalam transformasi objek-to-scene. Setelah kita mengubah koordinat 3D objek virtual menjadi bingkai koordinat dunia nyata, kita perlu memperkirakan pose kamera:
Kita perlu memahami posisi dan rotasi kamera karena itulah yang akan dilihat pengguna. Setelah kita memperkirakan pose kamera, kita siap untuk menempatkan adegan 3D ini pada gambar 2D.
Setelah kita memiliki transformasi ini, kita dapat membangun sistem yang lengkap.
Sebelum kita melanjutkan, kita perlu memahami cara memperkirakan pose kamera. Ini adalah langkah yang sangat penting dalam sistem augmented reality dan kita harus melakukannya dengan benar jika kita ingin hasilnya mulus. Di dunia augmented reality, kita melapisi sebuah objek baru di atas objek asli secara real time. Untuk melakukan itu, kita perlu mengetahui lokasi dan orientasi kamera, dan kita perlu melakukannya dengan cepat. Di sinilah estimasi pose menjadi sangat penting. Jika kita tidak melacak pose dengan benar, maka objek baru tidak akan terlihat alami.
Perhatikan gambar berikut:
Garis panah menunjukkan bahwa permukaan normal. Katakanlah objek mengubah orientasinya:
Sekarang meskipun lokasinya sama, orientasinya telah berubah. Kita perlu memiliki informasi ini agar objek baru terlihat alami. Kita perlu memastikan bahwa itu selaras dengan orientasi dan posisi ini.
Sekarang setelah Anda memahami apa itu estimasi pose, mari kita lihat bagaimana kita dapat menggunakannya untuk melacak objek planar. Mari kita perhatikan objek planar berikut:
Sekarang jika kita mengekstrak poin fitur dari gambar ini, kita akan melihat sesuatu seperti ini:
Mari kita miringkan kardusnya:
Seperti yang bisa kita lihat, kardus dimiringkan pada gambar ini. Sekarang jika kita ingin memastikan objek virtual kita terhampar di atas permukaan ini, kita perlu mengumpulkan informasi kemiringan planar ini. Salah satu cara untuk melakukannya adalah dengan menggunakan posisi relatif dari titik fitur tersebut. Jika kita mengekstrak titik fitur dari gambar sebelumnya, maka akan terlihat seperti ini:
Seperti yang kita lihat, titik fitur semakin dekat secara horizontal di ujung jauh bidang dibandingkan dengan yang di ujung dekat.
Jadi kita dapat memanfaatkan informasi ini untuk mengekstrak informasi orientasi dari gambar. Kita telah membahas transformasi perspektif secara mendetail ketika kita membahas transformasi geometris serta pencitraan panorama. Yang perlu kita lakukan adalah menggunakan kedua himpunan titik tersebut dan mengekstrak matriks homografinya. Matriks homografi ini akan memberi tahu kita bagaimana kardus berubah.
Perhatikan gambar berikut:
Kita mulai dengan memilih wilayah yang diminati.
Kita kemudian akan mengekstrak poin fitur dari wilayah yang diminati ini. Karena kita melacak objek planar, algoritma mengasumsikan bahwa wilayah yang diinginkan adalah bidang. Itu sudah jelas, tetapi lebih baik untuk menyatakannya secara eksplisit. Jadi, pastikan kita memiliki kardus di tangan kita saat memilih wilayah yang diminati ini. Selain itu, akan lebih baik jika kardus memiliki banyak pola dan titik-titik khas sehingga mudah untuk mendeteksi dan melacak titik-titik fitur di dalamnya. Biarkan pelacakan dimulai. Kita akan memindahkan kardus untuk melihat apa yang terjadi:
Seperti yang terlihat, poin fitur sedang dilacak di dalam wilayah yang diminati. Mari kita miringkan dan lihat apa yang terjadi:
Seperti yang kita lihat, poin fitur sedang dilacak dengan benar, persegi panjang yang dilapis mengubah orientasinya sesuai dengan permukaan kardus.
Di bawah ini coding untuk melakukannya:
#pose_estimation.py
import sys
from collections import namedtuple
import cv2
import numpy as np
class PoseEstimator(object):
def __init__(self):
# Use locality sensitive hashing algorithm
flann_params = dict(algorithm = 6, table_number = 6,
key_size = 12, multi_probe_level = 1)
self.min_matches = 10
self.cur_target = namedtuple('Current', 'image, rect, keypoints, descriptors, data')
self.tracked_target = namedtuple('Tracked', 'target, points_prev, points_cur, H, quad')
self.feature_detector = cv2.ORB_create(nfeatures=1000)
self.feature_matcher = cv2.FlannBasedMatcher(flann_params, {})
self.tracking_targets = []
# Function to add a new target for tracking
def add_target(self, image, rect, data=None):
x_start, y_start, x_end, y_end = rect
keypoints, descriptors = [], []
for keypoint, descriptor in zip(*self.detect_features(image)):
x, y = keypoint.pt
if x_start <= x <= x_end and y_start <= y <= y_end:
keypoints.append(keypoint)
descriptors.append(descriptor)
descriptors = np.array(descriptors, dtype='uint8')
self.feature_matcher.add([descriptors])
target = self.cur_target(image=image, rect=rect, keypoints=keypoints,
descriptors=descriptors, data=None)
self.tracking_targets.append(target)
# To get a list of detected objects
def track_target(self, frame):
self.cur_keypoints, self.cur_descriptors = self.detect_features(frame)
if len(self.cur_keypoints) < self.min_matches:
return []
matches = self.feature_matcher.knnMatch(self.cur_descriptors, k=2)
matches = [match[0] for match in matches if len(match) == 2 and
match[0].distance < match[1].distance * 0.75]
if len(matches) < self.min_matches:
return []
matches_using_index = [[] for _ in range(len(self.tracking_targets))]
for match in matches:
matches_using_index[match.imgIdx].append(match)
tracked = []
for image_index, matches in enumerate(matches_using_index):
if len(matches) < self.min_matches:
continue
target = self.tracking_targets[image_index]
points_prev = [target.keypoints[m.trainIdx].pt for m in matches]
points_cur = [self.cur_keypoints[m.queryIdx].pt for m in matches]
points_prev, points_cur = np.float32((points_prev, points_cur))
H, status = cv2.findHomography(points_prev, points_cur, cv2.RANSAC, 3.0)
status = status.ravel() != 0
if status.sum() < self.min_matches:
continue
points_prev, points_cur = points_prev[status], points_cur[status]
x_start, y_start, x_end, y_end = target.rect
quad = np.float32([[x_start, y_start], [x_end, y_start], [x_end, y_end], [x_start, y_end]])
quad = cv2.perspectiveTransform(quad.reshape(1, -1, 2), H).reshape(-1, 2)
track = self.tracked_target(target=target, points_prev=points_prev,
points_cur=points_cur, H=H, quad=quad)
tracked.append(track)
tracked.sort(key = lambda x: len(x.points_prev), reverse=True)
return tracked
# Detect features in the selected ROIs and return the keypoints and descriptors
def detect_features(self, frame):
keypoints, descriptors = self.feature_detector.detectAndCompute(frame, None)
if descriptors is None:
descriptors = []
return keypoints, descriptors
# Function to clear all the existing targets
def clear_targets(self):
self.feature_matcher.clear()
self.tracking_targets = []
class VideoHandler(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.paused = False
self.frame = None
self.pose_tracker = PoseEstimator()
cv2.namedWindow('Tracker')
self.roi_selector = ROISelector('Tracker', self.on_rect)
def on_rect(self, rect):
self.pose_tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.pose_tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, (255, 255, 255), 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, (255, 255, 255))
self.roi_selector.draw_rect(img)
cv2.imshow('Tracker', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.pose_tracker.clear_targets()
if ch == 27:
break
class ROISelector(object):
def __init__(self, win_name, callback_func):
self.win_name = win_name
self.callback_func = callback_func
cv2.setMouseCallback(self.win_name, self.on_mouse_event)
self.selection_start = None
self.selected_rect = None
def on_mouse_event(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.selection_start = (x, y)
if self.selection_start:
if flags & cv2.EVENT_FLAG_LBUTTON:
x_orig, y_orig = self.selection_start
x_start, y_start = np.minimum([x_orig, y_orig], [x, y])
x_end, y_end = np.maximum([x_orig, y_orig], [x, y])
self.selected_rect = None
if x_end > x_start and y_end > y_start:
self.selected_rect = (x_start, y_start, x_end, y_end)
else:
rect = self.selected_rect
self.selection_start = None
self.selected_rect = None
if rect:
self.callback_func(rect)
def draw_rect(self, img):
if not self.selected_rect:
return False
x_start, y_start, x_end, y_end = self.selected_rect
cv2.rectangle(img, (x_start, y_start), (x_end, y_end), (0, 255, 0), 2)
return True
if __name__ == '__main__':
VideoHandler().start()
cv2.destroyAllWindows()
Untuk memulainya, kita memiliki kelas PoseEstimator yang melakukan semua pekerjaan berat di sini. Kita membutuhkan sesuatu untuk mendeteksi fitur dalam gambar dan sesuatu untuk mencocokkan fitur antara gambar yang berurutan. Jadi kita menggunakan pendeteksi fitur ORB dan pencocokan fitur Flann. Seperti yang bisa kita lihat, kita menginisialisasi kelas dengan parameter ini di konstruktor.
Setiap kali kita memilih wilayah yang diminati, kita memanggil metode add_target untuk menambahkannya ke daftar target pelacakan kita. Metode ini hanya mengekstrak fitur dari wilayah yang diinginkan dan menyimpannya di salah satu variabel kelas. Sekarang kita memiliki target, kita siap untuk melacaknya!
Metode track_target menangani semua pelacakan. Kita mengambil frame saat ini dan mengekstrak semua keypoint. Namun, kita tidak terlalu tertarik dengan semua keypoint dalam frame video saat ini. Kita hanya ingin keypoint dari target objek kita. Jadi sekarang, tugas kita adalah menemukan keypoint terdekat dalam frame saat ini.
Kita sekarang memiliki satu set keypoint dari frame saat ini dan kita memiliki satu set keypoint lain dari objek target kita di frame sebelumnya. Langkah selanjutnya adalah mengekstrak matriks homografi dari titik-titik pencocokan tersebut. Matriks homografi ini memberi tahu kita cara mengubah persegi panjang yang dihamparkan sehingga sejajar dengan permukaan kardus. Kita hanya perlu mengambil matriks homografi ini dan menerapkannya pada persegi panjang yang dilapis untuk mendapatkan posisi baru dari semua poinnya.
Sekarang setelah kita tahu cara melacak objek planar, mari kita lihat cara melapisi objek 3D di atas dunia nyata. Objeknya adalah 3D tetapi video di layar kita adalah 2D. Jadi langkah pertama di sini adalah memahami cara memetakan objek 3D tersebut ke permukaan 2D agar terlihat realistis. Kita hanya perlu memproyeksikan titik 3D itu ke permukaan planar.
Setelah kita memperkirakan pose, kita akan memproyeksikan poin dari 3D ke 2D. Perhatikan gambar berikut:
Seperti yang bisa kita lihat di sini, remote control TV adalah objek 3D tetapi kita melihatnya di bidang 2D. Sekarang jika kita memindahkannya, itu akan terlihat seperti ini:
Objek 3D ini masih berada pada bidang 2D. Objek telah pindah ke lokasi yang berbeda dan jarak dari kamera juga berubah. Bagaimana kita menghitung koordinat ini? Kita membutuhkan mekanisme untuk memetakan objek 3D ini ke permukaan 2D. Di sinilah proyeksi 3D ke 2D menjadi sangat penting. Kita hanya perlu memperkirakan pose kamera awal untuk memulai. Sekarang, mari kita asumsikan bahwa parameter intrinsik kamera sudah diketahui. Jadi kita bisa menggunakan fungsi solvePnP di OpenCV untuk memperkirakan pose kamera. Fungsi ini digunakan untuk memperkirakan pose objek menggunakan sekumpulan titik. Anda dapat membaca lebih lanjut tentang itu di https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#bool%20solvePnP(InputArray%20objectPoints,%20InputArray%20imagePoints,%20InputArray%20cameraMatrix,%20InputArray%20distCoeffs,%20OutputArray%20rvec,%20OutputArray%20tvec,%20bool%20useExtrinsicGuess,%20int%20flags) Setelah kita melakukan ini, kita perlu memproyeksikan titik-titik ini ke 2D. Kita akan menggunakan projectPoints fungsi OpenCV untuk melakukan ini. Fungsi ini menghitung proyeksi titik-titik 3D tersebut ke bidang 2D.
Sekarang kita siap untuk membangun sistem final. Katakanlah kita ingin melapisi piramida di atas kardus kita seperti yang ditunjukkan di sini:
Mari kita miringkan kardus untuk melihat apa yang terjadi:
Piramida mengikuti permukaan. Mari tambahkan target kedua:
Anda dapat terus menambahkan lebih banyak target dan semua piramida itu akan dilacak dengan baik. Mari kita lihat bagaimana melakukan ini menggunakan OpenCV Python. Pastikan untuk menyimpan file sebelumnya sebagai pose_estimation.py karena kita akan mengimpor beberapa kelas dari sana.
#augmened_reality.py
import cv2
import numpy as np
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))],
-1, (0,255,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))],
-1, (255,0,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))],
-1, (0,0,150), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1],
verts_floor[4:5]))], -1, (255,255,0), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
class Tracker digunakan untuk melakukan semua perhitungan di sini. Kita menginisialisasi class dengan struktur piramida yang didefinisikan menggunakan edges (tepi) dan vertices (simpul). Logika yang kita gunakan untuk melacak permukaan sama dengan yang kita bahas sebelumnya karena kita menggunakan kelas yang sama. Kita hanya perlu menggunakan solvePnP dan projectPoints untuk memetakan piramida 3D ke permukaan 2D.
Sekarang setelah kita tahu cara menambahkan piramida virtual, mari kita lihat apakah kita dapat menambahkan beberapa gerakan. Mari kita lihat bagaimana kita dapat mengubah ketinggian piramida secara dinamis. Ketika kita mulai, piramida akan terlihat seperti ini:
Jika kita menunggu beberapa saat, piramida menjadi lebih tinggi dan akan terlihat seperti ini:
Mari kita lihat bagaimana melakukannya di OpenCV Python. Di dalam kode augmented reality yang baru saja kita bahas, tambahkan cuplikan berikut di akhir metode init dalam Tracker class:
Sekarang kita memiliki struktur, kita perlu menambahkan kode untuk mengubah ketinggian secara dinamis. Ganti metode overlay_graphics() dengan metode berikut:
Sekarang kita tahu bagaimana mengubah ketinggian, mari kita lanjutkan dan membuat piramida menari untuk kita. Kita bisa membuat ujung piramida berosilasi dengan cara periodik yang bagus. Jadi ketika kita mulai akan terlihat seperti ini:
Jika kita menunggu beberapa saat, tampilannya akan seperti ini:
Lihat coding augmented_reality_motion.py untuk mengetahui detail implementasi.
Dalam percobaan kita berikutnya, kita akan membuat seluruh piramida bergerak di sekitar wilayah yang diinginkan. Kita bisa membuatnya bergerak dengan cara apapun yang kita inginkan. Mari kita mulai dengan menambahkan gerakan diagonal linier di sekitar wilayah yang kita pilih. Ketika Anda mulai, itu akan terlihat seperti ini:
Setelah beberapa waktu, akan terlihat seperti ini:
Lihat augmented_reality_dancing.py untuk melihat cara mengubah metode overlay_graphics() untuk membuatnya menari. Mari kita lihat apakah kita bisa membuat piramida berputar-putar di sekitar wilayah yang kita minati. Ketika Anda mulai, itu akan terlihat seperti ini:
Setelah beberapa waktu, itu akan pindah ke posisi baru:
Silahkan merujuk ke augmented_reality_circular_motion.py untuk melihat cara mewujudkannya. Kita dapat membuatnya melakukan apa pun yang kita inginkan. kita hanya perlu menemukan rumus matematika yang tepat dan piramida akan benar-benar menari mengikuti irama kita. Kita juga dapat mencoba objek virtual lain untuk melihat apa yang dapat kita lakukan dengannya. Ada banyak hal yang dapat kitalakukan dengan banyak objek yang berbeda. Contoh-contoh di atas memberikan referensi untuk kita membangun banyak aplikasi augmented reality yang menarik.
#augmented_reality_variable_height.py
import cv2
import numpy as np
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
self.graphics_counter = 0
self.time_counter = 0
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
self.time_counter += 1
if not self.time_counter % 20:
self.graphics_counter = (self.graphics_counter + 1) % 8
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, self.graphics_counter]])
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))],
-1, (0,255,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))],
-1, (255,0,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))],
-1, (0,0,150), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1],
verts_floor[4:5]))], -1, (255,255,0), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
#augmented_reality_dancing.py
import cv2
import numpy as np
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
self.graphics_counter = 0.5
self.time_counter = 0
self.sign = 1
self.frame_jump = 3
self.movement_size = 1
self.step_size = 0.1
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
self.time_counter += 1
if not self.time_counter % self.frame_jump:
self.graphics_counter = self.graphics_counter + self.sign*self.step_size
if abs(self.graphics_counter) >= self.movement_size:
self.sign *= -1
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[self.graphics_counter, 0.5, 4]])
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))],
-1, (0,255,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))],
-1, (255,0,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))],
-1, (0,0,150), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1],
verts_floor[4:5]))], -1, (255,255,0), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
#augmented_reality_linear_motion.py
import cv2
import numpy as np
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
self.graphics_counter = 0.5
self.time_counter = 0
self.sign = 1
self.frame_jump = 3
self.movement_size = 1
self.step_size = 0.1
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
self.time_counter += 1
if not self.time_counter % self.frame_jump:
self.graphics_counter = self.graphics_counter + self.sign*self.step_size
if abs(self.graphics_counter) >= self.movement_size:
self.sign *= -1
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]]) + self.graphics_counter
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))],
-1, (0,255,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))],
-1, (255,0,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))],
-1, (0,0,150), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1],
verts_floor[4:5]))], -1, (255,255,0), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
#augmented_reality_circular_motion.py
import cv2
import numpy as np
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
self.time_counter = 0
self.frame_jump = 1
self.degrees_counter = 0
self.step_size_degrees = 3
self.radius = 1.0
self.xm = self.radius
self.ym = 0
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
self.time_counter += 1
if not self.time_counter % self.frame_jump:
self.degrees_counter += self.step_size_degrees
self.xm = (self.radius*np.cos(3.14*self.degrees_counter/180.0))
self.ym = (self.radius*np.sin(3.14*self.degrees_counter/180.0))
self.overlay_vertices = np.float32([[0+self.xm, 0+self.ym, 0], [0+self.xm, 1+self.ym, 0],
[1+self.xm, 1+self.ym, 0], [1+self.xm, 0+self.ym, 0],
[0.5+self.xm, 0.5+self.ym, 4]])
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))],
-1, (0,255,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))],
-1, (255,0,0), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))],
-1, (0,0,150), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1],
verts_floor[4:5]))], -1, (255,255,0), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
#Tugas
#Modifikasilah sedikit augmened_reality.py di atas secara bebas
#Misalnya Anda dapat menggantikan objek piramida dengan objek lain
#Atau objek piramidanya berpindah/bergerak dengan cara yang belum dibahas di atas
#Atau menambah objek lain bersama dengan piramida
#Atau mungkin ada ide-ide lainnya
#Silahkan pilih salah satunya saja.
#kumpulkan file ipynb ini melalui e-learning!
#code baru hasil modifikasi (DI SINI)
#augmented_reality_circular_motion.py
import cv2
import numpy as np
import random
from pose_estimation import PoseEstimator, ROISelector
class Tracker(object):
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.frame = None
self.paused = False
self.tracker = PoseEstimator()
cv2.namedWindow('Augmented Reality')
self.roi_selector = ROISelector('Augmented Reality', self.on_rect)
self.overlay_vertices = np.float32([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0],
[0.5, 0.5, 4]])
self.overlay_edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(0,4), (1,4), (2,4), (3,4)]
self.color_base = (0, 255, 0)
self.color_lines = (0, 0, 0)
self.time_counter = 0
self.frame_jump = 1
self.degrees_counter = 0
self.step_size_degrees = 3
self.radius = 1.0
self.xm = self.radius
self.ym = 0
def on_rect(self, rect):
self.tracker.add_target(self.frame, rect)
def start(self):
while True:
is_running = not self.paused and self.roi_selector.selected_rect is None
if is_running or self.frame is None:
ret, frame = self.cap.read()
scaling_factor = 0.5
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,
interpolation=cv2.INTER_AREA)
if not ret:
break
self.frame = frame.copy()
img = self.frame.copy()
if is_running:
tracked = self.tracker.track_target(self.frame)
for item in tracked:
cv2.polylines(img, [np.int32(item.quad)], True, self.color_lines, 2)
for (x, y) in np.int32(item.points_cur):
cv2.circle(img, (x, y), 2, self.color_lines)
self.overlay_graphics(img, item)
self.roi_selector.draw_rect(img)
cv2.imshow('Augmented Reality', img)
ch = cv2.waitKey(1)
if ch == ord(' '):
self.paused = not self.paused
if ch == ord('c'):
self.tracker.clear_targets()
if ch == 27:
break
def overlay_graphics(self, img, tracked):
x_start, y_start, x_end, y_end = tracked.target.rect
quad_3d = np.float32([[x_start, y_start, 0], [x_end, y_start, 0],
[x_end, y_end, 0], [x_start, y_end, 0]])
h, w = img.shape[:2]
K = np.float64([[w, 0, 0.5*(w-1)],
[0, w, 0.5*(h-1)],
[0, 0, 1.0]])
dist_coef = np.zeros(4)
ret, rvec, tvec = cv2.solvePnP(quad_3d, tracked.quad, K, dist_coef)
self.time_counter += 1
if not self.time_counter % self.frame_jump:
self.degrees_counter += self.step_size_degrees
self.xm = (self.radius*np.cos(3.14*self.degrees_counter/180.0))
self.ym = (self.radius*np.sin(3.14*self.degrees_counter/180.0))
self.overlay_vertices = np.float32([[0+self.xm, 0+self.ym, 0], [0+self.xm, 1+self.ym, 0],
[1+self.xm, 1+self.ym, 0], [1+self.xm, 0+self.ym, 0],
[0.5+self.xm, 0.5+self.ym, 4]])
verts = self.overlay_vertices * [(x_end-x_start), (y_end-y_start),
-(x_end-x_start)*0.3] + (x_start, y_start, 0)
verts = cv2.projectPoints(verts, rvec, tvec, K, dist_coef)[0].reshape(-1, 2)
verts_floor = np.int32(verts).reshape(-1,2)
cv2.drawContours(img, [verts_floor[:4]], -1, self.color_base, -3)
cv2.drawContours(img, [np.vstack((verts_floor[:2], verts_floor[4:5]))], -1, random_color(), -3)
cv2.drawContours(img, [np.vstack((verts_floor[1:3], verts_floor[4:5]))], -1, random_color(), -3)
cv2.drawContours(img, [np.vstack((verts_floor[2:4], verts_floor[4:5]))], -1, random_color(), -3)
cv2.drawContours(img, [np.vstack((verts_floor[3:4], verts_floor[0:1], verts_floor[4:5]))], -1, random_color(), -3)
for i, j in self.overlay_edges:
(x_start, y_start), (x_end, y_end) = verts[i], verts[j]
cv2.line(img, (int(x_start), int(y_start)), (int(x_end), int(y_end)), self.color_lines, 2)
def random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
if __name__ == '__main__':
Tracker().start()
cv2.destroyAllWindows()
Warna pada piramid tersebut akan digenerate menggunakan fungsi random_color() yang mengembalikan sebuah tuple dengan angka random setiap kali melakukan render. Sehingga menghasilkan piramid random yang bergerak secara sirkular.
